/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

// 定义常量
const PENDING ='pending';
const RESOLVED ='fulfilled';
const REJECTED ='rejected';

class MyPromise {
  // 构造函数
  constructor (executor) {
    // 捕获错误
    try {
      // executor 执行器，立即执行
      executor(this.resolve, this.reject);
    } catch (err) {
      this.reject(err);
    }
  }

  status = PENDING;
  value = undefined;
  reason = undefined;
  successCallBackArr = [];
  failCallBackArr = [];

  // 思路：改变状态，保存值并返回当作回调函数的参数
  resolve = (value) => {
    if (this.status !== PENDING) return;
    this.status = RESOLVED;
    this.value = value;

    // 判断回调数组里是否有值，如果有就按顺序取第一个执行
    while (this.successCallBackArr.length) this.successCallBackArr.shift()(this.value);
  }

  reject = (reason) => {
    if (this.status !== PENDING) return;
    this.status = REJECTED;
    this.reason = reason;

     // 判断回调数组里是否有值，如果有就按顺序取第一个执行
     while (this.failCallBackArr.length) this.failCallBackArr.shift()(this.reason);
  }

  // 思路：then返回的要是一个promise对象，链式调用
  then = (successCallBack, failCallBack) => {
    // then参数是可选的，兼容不传参数的情况
    successCallBack = successCallBack || (value => value);
    failCallBack = failCallBack || (reason => { throw reason });

    let newPromise = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === RESOLVED) {

        // 使用setTimeout是因为要把newPromise传递给resolvePromise函数，
        // 但是newPromise在new 的过程中还获取不到，所以使用setTimeout把这部分变成异步代码就能获取到了
        setTimeout(() => {
          // try {
          //   // 把回调返回的结果存为变量，传给resolve方法，就等于是传递给下一个then方法的回调函数做为参数
          //   let res = successCallBack(this.value);
          //   resolvePromise(newPromise, res, resolve, reject);
          // } catch (err) {
          //   reject(err);
          // }
          resolvePromise(successCallBack, newPromise, this.value, resolve, reject);
        }, 0);
      } else if (this.status === REJECTED) {

        setTimeout(() => {
          resolvePromise(failCallBack, newPromise, this.reason, resolve, reject);
        }, 0);
        
      } else {
        // pending时说明存在异步执行的代码
        this.successCallBackArr.push(() => {
          setTimeout(() => {
            // try {
            //   let res = successCallBack(this.value);
            //   resolvePromise(newPromise, res, resolve, reject);
            // } catch (err) {
            //   reject(err);
            // }
            resolvePromise(successCallBack, newPromise, this.value, resolve, reject);
          }, 0);
        });
        this.failCallBackArr.push(() => {
          setTimeout(() => {
            // try {
            //   let res = failCallBack(this.reason);
            //   resolvePromise(newPromise, res, resolve, reject);
            // } catch (err) {
            //   reject(err);
            // }
            resolvePromise(failCallBack, newPromise, this.reason, resolve, reject);
          }, 0);
        });
      }
    });
    return newPromise;
  }

  // 1.  finally其实就是一个promise的then方法的别名，在执行then方法之前，先处理callback函数.不管是成功还是失败状态，finally的回调函数都会在最后执行一次
  // 2. finall方法后可以调用then方法拿到最终返回的结果
  // 3. callback可能是异步操作，所以不管callback是什么操作，都用resolve静态方法给他转成promise再返回
  finally = (callback) => {
    return this.then(v => {
      return MyPromise.resolve(callback()).then(() => v);
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason });
    });
  }

  catch = (failcallback) => {
    return this.then(undefined, failcallback);
  }

  // 思路：循环处理传进来的数组，注意判断是否是promise对象，还有等循环可能会有异步操作，所以要等所有完成后再去执行resolve
  // all是所有的成功才成功，有一个失败就是失败的
  static all(array) {
    let res = [];
    let idx = 0;
    return new MyPromise((resolve, reject) => {
      function addData (key, val) {
        res[key] = val;
        idx++;
        if (idx === array.length) resolve(res);
      }

      for (let i = 0; i < array.length; i++) {
        const item = array[i];
        if (item instanceof MyPromise) {
          item.then(v => addData(i, v), reason => reject(reason));
        } else {
          addData(i, item);
        }
      }
    });
  }

  static resolve(value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
  }

  static reject(value) {
    return new MyPromise((resolve, reject) => reject(value));
  }
}

// 判断返回值是普通值还是promise对象的处理函数
// function resolvePromise (newPromise, val, resolve, reject) {
//   // 先判断是否自己返回了自己的情况
//   if (val === newPromise) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
  
//   if (val instanceof MyPromise) {
//     // 如果是promise对象
//     val.then(resolve, reject); // 等同于 val.then(value => { resolve(value) }, { reason => reject(reason) })
//   } else {
//     // 如果是普通值
//     resolve(val);
//   }
// }

// function myCallBack (callback, newPromise, value, resolve, reject) {
//   try {
//     // 把回调返回的结果存为变量，传给resolve方法，就等于是传递给下一个then方法的回调函数做为参数
//     let res = callback(value);
//     resolvePromise(newPromise, res, resolve, reject);
//   } catch (err) {
//     reject(err);
//   }
// }

function resolvePromise (callback, newPromise, value, resolve, reject) {
  try {
    // 把回调返回的结果存为变量，传给resolve方法，就等于是传递给下一个then方法的回调函数做为参数
    let val = callback(value);
    // 先判断是否自己返回了自己的情况
    if (val === newPromise) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
    
    if (val instanceof MyPromise) {
      // 如果是promise对象
      val.then(resolve, reject); // 等同于 val.then(value => { resolve(value) }, { reason => reject(reason) })
    } else {
      // 如果是普通值
      resolve(val);
    }
  } catch (err) {
    reject(err);
  }
}

module.exports = MyPromise;